# External Modules
import pygame
import time
import sys
from pygame.locals import *

# Project Modules
from CPlayer import CPlayer
from CMap import CMap
from CHUD import CHUD
from CTextureManager import CTextureManager

class CGame(object):
    """ Holds our high-level game information and objects. """
    #Tile, Column, Vision Radius information
    TILE_SIZE = 16
    COLUMNS = 40 
    ROWS = 26
    RADIUS = 4
    
    # Window Constants
    BUILD_NAME = "WildMoose Roguelike GameJamb"
    RESX = 640
    RESY = 480
    
    #The colors, Duke, the colors
    BLACK = (0,0,0)
    
    # Character Constants
    PLAYER_SPRITE_SHEET = "img/char.gif"
    
    # Static HUD variable to give other objects easy access
    mHUD = None
    
    # Our game states
    GS_TITLE = 0
    GS_DEAD = 1
    GS_GAME = 2
    GS_WON = 3
    GS_WONG = 4 # good ending
    
    # Maximum levels in the dungeon
    DUNGEON_DEPTH = 10
      
    def __init__(self):
        
        # Initialize PyGame and get a drawing surface/window
        pygame.init()
        self.mScreen = pygame.display.set_mode( ( self.RESX , self.RESY ))
        pygame.display.set_caption( self.BUILD_NAME )
        
        # Initialize our event variable that we will use for polling
        self.mCurrentEvent = pygame.event.poll()
        
        # Set to title screen
        self.mCurrentGameState = CGame.GS_TITLE
        
        # Start music
        pygame.mixer.music.load("audio/CaveCrunch.xm")
        pygame.mixer.init(44100, -16, 1, 1024)
        pygame.mixer.music.set_volume(1.0)
        pygame.mixer.music.play(-1,0.0)
        
        
        # Initialize time variables
        self.mFrameCount = 0
        self.mFrameRate = 0
        self.mOldTime = time.clock()
        
        # Turn and step variables to manage gameplay
        self.mCurrentStep = 0
        
        # The level we are currently on and the max level
        self.mCurrentDepth = 1
        
        # Initialize HUD
        CGame.mHUD = CHUD()
        CHUD.LogMessage("HUD Initialized")
        self.mHUD.SetTitleScreenText( ['Welcome to WildMoose! This game was made in about a week!' , '[Arrows]->Move   [Shift]->Turn   [Space]->Attack   [F]->Fullscreen' ,'Made by: Daniel Cox and Lina Adkins' , 'Please press [SPACE] to begin your adventure!'])
        
    def Start(self):
        """ Start the actual dungeon crawl portion of the game. Used to reset as well. """
        # Turn and step variables to manage gameplay
        self.mCurrentStep = 0
        
        # Initialize Player Object
        self.mPlayer = CPlayer( self.PLAYER_SPRITE_SHEET )
        CHUD.LogMessage("Player Created")
        
        # Track our current dungeon level
        self.mCurrentDepth = 1
        CHUD.ChangeStat( CHUD.STAT_LEVEL , self.mCurrentDepth )
        
        # Initialize Map Object
        self.mMap = CMap( self.mPlayer , self.mCurrentDepth )
        CHUD.LogMessage("Map Loaded")
        self.mPlayer.SetCurrentMap( self.mMap )
        
        # Clear the darkness around our character
        self.position = self.mPlayer.GetWorldPos()
        self.mMap.clear_block(self.position)
        
        # Start our game
        self.mCurrentGameState = CGame.GS_GAME


    def ProcessEvent(self):
        """ Process all events not relating to input. """
        
        # If our event is a keypress, hand it to ProcessInput().
        if( self.mCurrentEvent.type == pygame.KEYDOWN ): 
            self.ProcessInput()
            return
        # Catch-all for other events
        else:
            # 'X' button or other window close message sent
            if( self.mCurrentEvent.type == pygame.QUIT):
                    self.Quit()
                        
            
    def DrawDarkness(self):
        ''' Draws the darkness and shadows on the board. 0 is dark, 1 is in shadows, 2 is illuminatron
        '''   
        # Draw black tiles
        for row in range(self.ROWS):
            for col in range(self.COLUMNS):
                if self.mMap.cleared[row][col] == 0:
                    if not self.mMap.current[row][col]:
                        pygame.draw.rect(self.mScreen, self.BLACK, (col*self.TILE_SIZE, row*self.TILE_SIZE, self.TILE_SIZE, self.TILE_SIZE))  
                if self.mMap.cleared[row][col] == 1:
                    if not self.mMap.current[row][col]:
                        shadow = pygame.Surface((self.TILE_SIZE, self.TILE_SIZE))
                        shadow.set_alpha(200)
                        shadow.fill(self.BLACK)
                        self.mScreen.blit(shadow, (col*self.TILE_SIZE, row*self.TILE_SIZE))
                        
    def GoToNextLevel(self):
        """ Generate the next level when the user goes down the stairs. """
        
        # If you make it to the last set of stairs then you win
        if( self.mCurrentDepth == CGame.DUNGEON_DEPTH ):
            self.mCurrentGameState = CGame.GS_WON;
            # Play victory music
            pygame.mixer.music.load("audio/Squirm.xm")
            pygame.mixer.init(44100, -16, 1, 1024)
            pygame.mixer.music.play(-1,0.0)
            return
        
        self.mCurrentDepth += 1
        
        # Initialize Map Object
        self.mMap = CMap( self.mPlayer , self.mCurrentDepth )
        self.mPlayer.SetCurrentMap( self.mMap )
        CHUD.LogMessage("Level " + str(self.mCurrentDepth) + " reached!")
        
        # Clear the darkness around our character
        self.position = self.mPlayer.GetWorldPos()
        self.mMap.clear_block(self.position)
        
        # Track our current dungeon level
        CHUD.ChangeStat( CHUD.STAT_LEVEL , self.mCurrentDepth )
    
    def ProcessInput(self):
        """ Process user input. """
        # Todo - Cache player position since it is used so often
        # If we are playing the game
        if( self.mCurrentGameState == CGame.GS_GAME ):
            
            # Arrow Up
            if( self.mCurrentEvent.key == pygame.K_UP ):
                # These blocks are for detecting if shift is pressed, and just changing dir instead of moving if so.
                if( self.mCurrentEvent.mod & pygame.KMOD_SHIFT ):
                    self.mPlayer.setDirection( self.mPlayer.UP )
                else:
                    # This block checks to see if there is a solid space where we are trying to move before moving
                    if( self.mMap.isSolidSpace( ( self.mPlayer.GetWorldPos()[0] , self.mPlayer.GetWorldPos()[1]-1 )  ) == False ) :
                        self.mPlayer.move( self.mPlayer.UP )
                        self.Step()
            
            # Arrow Down
            elif( self.mCurrentEvent.key == pygame.K_DOWN ):
                if self.mCurrentEvent.mod & pygame.KMOD_SHIFT:
                    self.mPlayer.setDirection( self.mPlayer.DOWN )
                else:
                    if( self.mMap.isSolidSpace( ( self.mPlayer.GetWorldPos()[0] , self.mPlayer.GetWorldPos()[1]+1 )  ) == False ) :
                        self.mPlayer.move( self.mPlayer.DOWN )
                        self.Step()
        
            # Arrow Left
            elif( self.mCurrentEvent.key == pygame.K_LEFT ):
                if( self.mCurrentEvent.mod & pygame.KMOD_SHIFT ):
                    self.mPlayer.setDirection( self.mPlayer.LEFT)
                else:
                    if( self.mMap.isSolidSpace( ( self.mPlayer.GetWorldPos()[0]-1 , self.mPlayer.GetWorldPos()[1] )  ) == False ) :
                        self.mPlayer.move( self.mPlayer.LEFT )
                        self.Step()
        
            # Arrow Right
            elif( self.mCurrentEvent.key == pygame.K_RIGHT):
                if( self.mCurrentEvent.mod & pygame.KMOD_SHIFT ):
                    self.mPlayer.setDirection( self.mPlayer.RIGHT )
                else:
                    if( self.mMap.isSolidSpace( ( self.mPlayer.GetWorldPos()[0]+1 , self.mPlayer.GetWorldPos()[1] )  ) == False ) :
                        self.mPlayer.move( self.mPlayer.RIGHT )
                        self.Step()
            
            # Space Key - Attack       
            if( self.mCurrentEvent.key == pygame.K_SPACE ):
                self.mPlayer.Attack()
                self.Step()
            
            # Toggle fullscreen with f    
            if( self.mCurrentEvent.key == pygame.K_f ):
                self.toggle_fullscreen()
             
        # All other gamestates just let you restart with space        
        else:
            # Space Key - Restart
            if( self.mCurrentEvent.key == pygame.K_SPACE ):
                # Restart music
                pygame.mixer.music.load("audio/CaveCrunch.xm")
                pygame.mixer.init(44100, -16, 1, 1024)
                pygame.mixer.music.set_volume(1.0)
                pygame.mixer.music.play(-1,0.0)
        
                self.Start()

        # Escape Key - Exit
        if self.mCurrentEvent.key == pygame.K_ESCAPE: self.Quit() 
                    
   

    def Draw(self):
        """ Render our visual elements. """
        
        # Clear Screen
        self.mScreen.fill( ( 0 , 0 , 0 ) )
        
        # --- Start Game Drawing Code---  
        if( self.mCurrentGameState == CGame.GS_GAME ):
            self.mMap.draw( self.mScreen )
            self.mPlayer.draw( self.mScreen )
            self.DrawDarkness() 
            self.mHUD.draw( self.mScreen )        
        # --- End Game Drawing Code ---
        
        # If we are in another state, draw the title screen
        else:
            self.mHUD.DrawTitleScreen( self.mScreen )
        
        #Flip our double-buffer
        pygame.display.flip()

        
    def Update(self):
        """ Run and update our game logic. """
        
        # Poll for an event and then process it accordingly
        self.mCurrentEvent = pygame.event.poll()
        self.ProcessEvent()
        
        self.mFrameCount += 1
        
        # Every 500 frames update our FPS & Timers
        if self.mFrameCount % 500 == 0:
            self.mNewTime = time.clock()
            self.mFrameRate = 500 / ( self.mNewTime - self.mOldTime )
            self.mOldTime = self.mNewTime
            pygame.display.set_caption( self.BUILD_NAME + " - FPS: " + str( self.mFrameRate ) + " - Step: " + str( self.mCurrentStep ) ) # Update our title bar to include FPS/Step Number
            
        # If we are in the game check to see if our player is dead and set the appropriate state.
        if( self.mCurrentGameState == CGame.GS_GAME and self.mPlayer.mCurrentHealth < 1 ):
            self.mCurrentGameState = CGame.GS_DEAD
            # Set our death title message!
            self.mHUD.SetTitleScreenText( ['Unfortunately you died! It\'s not the end of the world.' , 'Actually I guess it kind of is- how unlucky!' ,'Thankfully if you recite this magic spell you will rise again:' , '"SPACEBARSPACEBARSPACEBARSPACEBAR"'])
            
        # If we have won the game, display winning message
        if( self.mCurrentGameState == CGame.GS_WON ):
            self.mHUD.SetTitleScreenText(['Conglaturations','You flee from the dungeon like the coward that you are. Good job!', 'Next time try defeating the boss!' , 'GAME OVER']);
        
        # Actually display the good ending
        if( self.mCurrentGameState == CGame.GS_WONG):
            self.mHUD.SetTitleScreenText(['Congratulations!','You chose to defeat the boss instead of fleeing like a coward!', 'You are the true hero of our time!' , 'GAME OVER']);
        
    def Quit(self):
        """ Performs the necessary actions to quit the game. """
        # Todo : Some cleanup maybe?
        CTextureManager.FreeAllTextures()
        sys.exit(0)
        
    def toggle_fullscreen(self):
        """ Toggle fullscreen. Credit to Duoas for this easy snippet! """
        
        screen = pygame.display.get_surface()
        tmp = screen.convert()
        caption = pygame.display.get_caption()
        cursor = pygame.mouse.get_cursor()  # Duoas 16-04-2007 
        
        w,h = screen.get_width(),screen.get_height()
        flags = screen.get_flags()
        bits = screen.get_bitsize()
        
        pygame.display.init()
        
        screen = pygame.display.set_mode((w,h),flags^FULLSCREEN,bits)
        screen.blit(tmp,(0,0))
        pygame.display.set_caption(*caption)
     
        pygame.key.set_mods(0) #HACK: work-a-round for a SDL bug??
     
        pygame.mouse.set_cursor( *cursor )  # Duoas 16-04-2007
    
        return screen
        
    def Step(self):
        """ End our player's turn and advance the game a single step """
        self.mCurrentStep += 1
        
        # Check to see if we have defeated the boss, if so, set gamestate
        if( self.mPlayer.mDefeatedBoss == True):
            self.mCurrentGameState = CGame.GS_WONG
            # Play victory music
            pygame.mixer.music.load("audio/Squirm.xm")
            pygame.mixer.init(44100, -16, 1, 1024)
            pygame.mixer.music.set_volume(1.0)
            pygame.mixer.music.play(-1,0.0)
        
        # Clear darkness map at our position
        self.position = self.mPlayer.GetWorldPos()
        self.mMap.clear_block(self.position)
        
        # Get our enemies from our scene/map and step them
        for e in self.mMap.GetEnemies():
            e.Step()
            
        # Step our pickups to clean them up
        for p in self.mMap.GetPickups():
            p.Step()
            
        # Check to see if we are on the exit
        if( self.mMap.isStairSpace( ( self.position[1] , self.position[0] ) ) ):
            self.GoToNextLevel()
            
        # Check to see if we are on a pickup so we can pick it up
        if( self.mMap.isPickupInSpace( (self.position[1] , self.position[0])  ) ):
            pickup = self.mMap.GetPickupInSpace(  (self.position[1] , self.position[0])  )
            pickupName = pickup.GetName()
            pickupValue = pickup.GetValue()
            
            # Our logic that decides what action to take based on what item we have
            
            # If we have gold
            if( pickupName == "Gold" ):
                CHUD.LogMessage("Picked up " + pickupName + "! (" + str(pickupValue) + ")")
                self.mPlayer.mCurrentGold += pickupValue
                CHUD.ChangeStat( CHUD.STAT_GOLD , self.mPlayer.mCurrentGold )
            elif( pickupName == "Health"):
                CHUD.LogMessage("Picked up health! (" + str(pickupValue) + ")")
                self.mPlayer.mCurrentHealth += pickupValue
                CHUD.ChangeStat( CHUD.STAT_HEALTH , self.mPlayer.mCurrentHealth )
            elif( pickupName == "GoldSword"):
                self.mPlayer.GiveWeapon("Goldleaf" , 2)
                CHUD.LogMessage("Picked up Goldleaf (ATK+2)")
                self.mPlayer.UpdateCharacterStatsHUD()
            else:
                CHUD.LogMessage("Picked up a mysterious item!")
                
            pickup.Pickup()
        


            
        
        
         

